//
// Created by chao.li on 2023/11/2.
//

#ifndef SRC_IFS_DRAWER_IFS_H
#define SRC_IFS_DRAWER_IFS_H

#include "../drawer_basic.h"
#include <functional>

namespace ifs
{
    class ifs_rule
    {
    public:
        float p;
        virtual void process(double ix, double iy, double& ox, double& oy) = 0;
    };

    class ifs_rule_linear
        :public ifs_rule
    {
        double ff[6];
    public:
        /// xx: a b c d e f
        ifs_rule_linear(std::initializer_list<double> l, float s){
            int i = 0;
            for (auto f : l) {
                ff[i++] = f;
            }
            p = s;
        }

        ifs_rule_linear(const ifs_rule_linear& other){
            int i = 0;
            for (auto f : other.ff) {
                ff[i++] = f;
            }
            p = other.p;
        }

        /// x1 = a * x + b * y + e;
        /// y1 = c * x + d * y + f;
        virtual void process(double ix, double iy, double& ox, double& oy) override
        {
            ox = ff[0] * ix + ff[1] * iy + ff[4];
            oy = ff[2] * ix + ff[3] * iy + ff[5];
        }
    };

    class ifs_rule_func
        :public ifs_rule
    {
        std::function<void(double, double, double&, double&)> fun;
    public:
        /// xx: a b c d e f
        ifs_rule_func(std::function<void(double, double, double&, double&)> ff, float s){
            fun = ff;
            p = s;
        }

        ifs_rule_func(const ifs_rule_func& other){
            fun = other.fun;
            p = other.p;
        }
        virtual void process(double ix, double iy, double& ox, double& oy) override
        {
            fun(ix, iy, ox, oy);
        }
    };

    class ifs_rules
    {
        std::vector<std::shared_ptr<ifs_rule>> rules;

        std::shared_ptr<ifs_rule> random_rule()
        {
            float ra = (rand() % 100) / 100.f;
            for (int i = 0; i < rules.size(); ++i) {
                if(rules[i]->p > ra){
                    return rules[i];
                } else{
                    ra -= rules[i]->p;
                }
            }
            return nullptr;
        }
    public:
        template<class rType>
        ifs_rules(std::initializer_list<rType> rs)
        {
            float total_p = 0;
            for (rType r : rs) {
                rules.push_back(std::make_shared<rType>(r));
                total_p += r.p;
            }

            //概率归一化
            for (auto& p : rules) {
                p->p /= total_p;
            }
        }
        void process(double& x, double& y)
        {
            auto rule = random_rule();
            rule->process(x, y, x,  y);
        }
    };

    class drawer_ifs
            :public drawer_basic
    {
        std::shared_ptr<ifs_rules> rules;
    public:
        template<class rType>
        void init_rule(std::initializer_list<rType> rs)
        {
            rules = std::make_shared<ifs_rules>(rs);
        }

        void draw(const vec2& init, int times, const vec2& off, const vec2& scale, int start = -1)
        {
            auto x = init.x;
            auto y = init.y;
            for (int i = 0; i < times; ++i) {
                rules->process(x, y);
                if(i > start){
                    vec2 point = off + vec2(x, y)* scale;
                    point = vec2(round(point.x), round(point.y));
                    draw_point(point);
                }
            }
        }
    };

}

#endif //SRC_IFS_DRAWER_IFS_H
